﻿using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using NewRetail.Application.Components.Goods.Entity;
using NewRetail.Application.Components.Platform.Entity;
using NewRetail.Application.Components.Warehouse.Entity;
using NewRetail.Application.Components.Warehouse.Model;
using NewRetail.Application.Core;
using NewRetail.Application.Core.SingleStore;
using NewRetail.Application.Worker;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;

namespace NewRetail.Application.Components.Warehouse
{
    public class WarehouseGoodsDtoBase : EntityDto
    {
        public int operatorId { get; set; }
        public string receiptNo { get; set; }
    }

    public class ChangeWarehouseDto : WarehouseGoodsDtoBase
    {
        //
    }

    public class CheckStockDto : WarehouseGoodsDtoBase
    {
        //
    }

    public class InoutWarehouseDto : WarehouseGoodsDtoBase
    {
        //
        #region add by cl 20221027
        public bool isAutoPrint { get; set; } = false;
        #endregion
    }

    public class AdjustPriceDto : WarehouseGoodsDtoBase
    {
        //
    }

    #region add by cl
    /// <summary>
    /// 其他单审核反审输入参数
    /// </summary>
    public class OtherBillDto : WarehouseGoodsDtoBase
    {
        //
    }
    /// <summary>
    /// 次品单审核反审输入参数
    /// </summary>
    public class DefectiveBillDto : WarehouseGoodsDtoBase
    {
        //
    }
    #endregion

    public class WarehouseGoodsPagingRequestDto : PagingRequestDto
    {
        public string warehouseName { get; set; }
        public string warehouseNo { get; set; }
        public string categoryNo { get; set; }
        public string goodsName { get; set; }
        public string goodsNo { get; set; }
        public string goodsBarCode { get; set; }
        public string supplierId { get; set; }
        //是否打折 0不打折  1打折 0或1之外的为所有
        public string isDiscount { get; set; }
        //add by cl 20240307
        //建档日期开始
        public DateTime? createDate { get; set; }
        //建档日期结束
        public DateTime? createDateEnd { get; set; }
        //库存量下限
        public int? lowerQuantity { get; set; }
        //库存量上限
        public int? upperQuantity { get; set; }

        public bool HasWarehouseCondition() =>
            !(string.IsNullOrWhiteSpace(this.warehouseName) &&
            string.IsNullOrWhiteSpace(this.warehouseNo));

        public bool HasGoodsCondition() =>
            !(string.IsNullOrWhiteSpace(this.categoryNo) &&
            string.IsNullOrWhiteSpace(this.goodsName) &&
            string.IsNullOrWhiteSpace(this.goodsNo) &&
            string.IsNullOrWhiteSpace(this.goodsBarCode)&&
            this.createDate == null);
    }

    public class UpdateWarehouseGoodsStatusDto : IEntityDto
    {
        public string warehouseId { get; set; }
        public string goodsId { get; set; }
        public bool? isDiscount { get; set; }
    }

    public interface IWarehouseGoodsService : IApplicationService
    {
        Task ApproveChangeWarehouse(ChangeWarehouseDto dto);
        Task RevokeChangeWarehouse(ChangeWarehouseDto dto);

        Task ApproveCheckStock(CheckStockDto dto);
        Task RevokeCheckStock(CheckStockDto dto);

        Task ApproveInoutWarehouse(InoutWarehouseDto dto);
        Task<string> RevokeInoutWarehouse(InoutWarehouseDto dto);

        Task ApproveAdjustPrice(AdjustPriceDto dto);
        Task RevokeAdjustPrice(AdjustPriceDto dto);

        Task<WarehouseGoods[]> AttachWarehouse(WarehouseGoods[] data);
        Task<WarehouseGoods[]> AttachGoodsInfo(WarehouseGoods[] data);
        Task<WarehouseGoods[]> AttachSupplier(WarehouseGoods[] data);

        Task UpdateWarehouseGoodsStatus(UpdateWarehouseGoodsStatusDto[] dto);

        #region add by cl
        /// <summary>
        /// 其他单审核
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task ApproveOtherBill(OtherBillDto dto);
        /// <summary>
        /// 其他单反审
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task RevokeOtherBill(OtherBillDto dto);
        #endregion

        #region add by cl 次品单
        /// <summary>
        /// 次品单审核
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task ApproveDefectiveBill(DefectiveBillDto dto);
        /// <summary>
        /// 次品单反审
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        Task RevokeDefectiveBill(DefectiveBillDto dto);
        #endregion

        #region add by cl 20220729
        /// <summary>
        /// 类别库存查询
        /// </summary>
        /// <param name="inputDto"></param>
        /// <returns></returns>
        Task<IEnumerable<CategoryWarehouseGoodShowDto>> GetCategoryWarehouseGoodInfo(QueryCategoryWarehouseGoodDto inputDto);
        #endregion

        /// <summary>
        /// 获取实时库存信息
        /// </summary>
        /// <param name="inputDto"></param>
        /// <returns></returns>
        Task<PagedResultDto<CategoryRealWarehouseGoodShowDto>> GetCategoryRealWarehouseGoodInfo(WarehouseGoodsPagingRequestDto inputDto);
    }

    public class WarehouseGoodsService : MyApplicationService, IWarehouseGoodsService
    {
        private readonly IRepository<WarehouseGoods> warehouseGoods;
        private readonly ReceiptsHelper receiptsHelper;
        private readonly INewRetailEventBus newRetailEventBus;
        private readonly ISingleStoreService singleStoreService;
        public WarehouseGoodsService(IRepository<WarehouseGoods> warehouseGoods, INewRetailEventBus newRetailEventBus, ReceiptsHelper receiptsHelper, ISingleStoreService singleStoreService)
        {
            this.warehouseGoods = warehouseGoods;
            this.newRetailEventBus = newRetailEventBus;
            this.receiptsHelper = receiptsHelper;
            this.singleStoreService = singleStoreService;
        }

        public async Task UpdateWarehouseGoodsStatus(UpdateWarehouseGoodsStatusDto[] dto)
        {
            dto.Should().NotBeNullOrEmpty();

            var db = await this.warehouseGoods.GetDbContextAsync();
            var set = db.Set<WarehouseGoods>();
            foreach (var m in dto)
            {
                var goods = await set.FirstOrDefaultAsync(x => x.warehouseID == m.warehouseId && x.goodsID == m.goodsId);
                if (goods == null)
                {
                    continue;
                }

                if (m.isDiscount != null)
                {
                    goods.isDiscount = m.isDiscount.Value;
                }

                this.EntityHelper.SetUpdationInfo(goods);
            }

            await db.SaveChangesAsync();
        }

        public async Task<WarehouseGoods[]> AttachGoodsInfo(WarehouseGoods[] data)
        {
            var db = await this.warehouseGoods.GetDbContextAsync();

            await db.AttachData<GoodsInfo, WarehouseGoods>(data,
                idGetter: x => x.goodsID,
                action: (m, arr) => m.GoodsInfo = arr.FirstOrDefault());

            //添加分类
            var ids = data.Select(x => x.GoodsInfo.categoryID).ToArray();
            var goodsCategorys = await db.Set<GoodsCategory>().Where(x => ids.Contains(x.Id)).ToArrayAsync();
            foreach (var item in data)
            {
                item.GoodsInfo.GoodsCategory = goodsCategorys.Where(x => x.Id == item.GoodsInfo.categoryID && x.warehouseID == item.warehouseID).FirstOrDefault();
            }

            return data;
        }

        public async Task<WarehouseGoods[]> AttachWarehouse(WarehouseGoods[] data)
        {
            var db = await this.warehouseGoods.GetDbContextAsync();

            await db.AttachData<Warehouse.Entity.Warehouse, WarehouseGoods>(data,
                idGetter: x => x.warehouseID,
                action: (m, arr) => m.Warehouse = arr.FirstOrDefault());

            return data;
        }

        public async Task<WarehouseGoods[]> AttachSupplier(WarehouseGoods[] data)
        {
            if (!data.Any())
            {
                return data;
            }

            string warehouseid = this.singleStoreService.CurrentStore;
            var warehouseGoodsIds = data.Select(x => $"{x.warehouseID}{x.goodsID}").ToArray();

            var db = await this.warehouseGoods.GetDbContextAsync();
            var inOutWarehousings = await this.QueryInOutWarehousingsByIds(warehouseGoodsIds);
            if (string.IsNullOrEmpty(warehouseid))
            {
                foreach (var m in data)
                {
                    m.Supplier = inOutWarehousings.Where(x => $"{m.warehouseID}{m.goodsID}".Contains($"{x.warehouseID}{x.goodsID}")).Select(s => s.Supplier).FirstOrDefault();
                }
            }
            else
            {
                foreach (var m in data)
                {
                    m.Supplier = inOutWarehousings.Where(x => $"{m.warehouseID}{m.goodsID}".Contains($"{x.warehouseID}{x.goodsID}") && x.warehouseID == warehouseid).Select(s => s.Supplier).FirstOrDefault();
                }
            }

            return data;
        }

        async Task<InOutWarehousing[]> QueryInOutWarehousingsByIds(string[] ids)
        {
            var db = await this.warehouseGoods.GetDbContextAsync();

            var inOutWarehousings = await db.Set<InOutWarehousing>().ToArrayAsync();
            inOutWarehousings = inOutWarehousings.Where(x => ids.Contains($"{x.warehouseID}{x.goodsID}")).ToArray();

            var suppliers = await db.Set<Supplier>().Where(x => inOutWarehousings.Select(x => x.supplierID).Contains(x.Id)).ToArrayAsync();

            foreach (var m in inOutWarehousings)
            {
                m.Supplier = suppliers.FirstOrDefault(x => x.Id == m.supplierID);
            }

            return inOutWarehousings;
        }

        async Task ChangeWarehouse(ChangeWarehouse changeEntity, DbContext db)
        {
            var set = db.Set<WarehouseGoods>().Where(x => x.goodsID == changeEntity.goodsID);
            var createFromWarehouseGoods = false;
            var createToWarehouseGoods = false;

            var fromId = string.Empty;
            var toId = string.Empty;

            if (changeEntity.isAuditing)
            {
                fromId = changeEntity.outWarehouseID;
                toId = changeEntity.inWarehouseID;
            }
            else
            {
                fromId = changeEntity.inWarehouseID;
                toId = changeEntity.outWarehouseID;
            }

            var fromWarehouse = await set.OrderByDescending(x => x.addTime).FirstOrDefaultAsync(x => x.warehouseID == fromId);
            if (fromWarehouse == null)
            {
                createFromWarehouseGoods = true;
                fromWarehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = fromId
                };
                this.EntityHelper.InitEntity(fromWarehouse);
            }

            var toWarehouse = await set.OrderByDescending(x => x.addTime).FirstOrDefaultAsync(x => x.warehouseID == toId);
            if (toWarehouse == null)
            {
                createToWarehouseGoods = true;
                toWarehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = toId
                };
                this.EntityHelper.InitEntity(toWarehouse);
            }

            fromWarehouse.goodsNum -= changeEntity.quantity;
            toWarehouse.goodsNum += changeEntity.quantity;

            if (createFromWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(fromWarehouse);
            }
            if (createToWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(toWarehouse);
            }
        }

        public async Task ApproveChangeWarehouse(ChangeWarehouseDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<ChangeWarehouse>(input, async (db, change) =>
            {
                await ChangeWarehouse(change, db);
            });
        }

        public async Task RevokeChangeWarehouse(ChangeWarehouseDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<ChangeWarehouse>(input, async (db, change) =>
            {
                await ChangeWarehouse(change, db);
            });
        }

        async Task CheckStock(CheckStock changeEntity, DbContext db)
        {
            var warehouse = await db.Set<WarehouseGoods>()
                .OrderByDescending(x => x.addTime)
                .Where(x => x.goodsID == changeEntity.goodsID && x.warehouseID == changeEntity.warehouseID)
                .FirstOrDefaultAsync();

            var createWarehouseGoods = false;

            if (warehouse == null)
            {
                createWarehouseGoods = true;
                warehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = changeEntity.warehouseID
                };
                this.EntityHelper.InitEntity(warehouse);
            }

            if (changeEntity.isAuditing)
            {
                warehouse.goodsNum = changeEntity.newNum;
            }
            else
            {
                warehouse.goodsNum = changeEntity.oldNum;
            }

            if (createWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(warehouse);
            }
        }

        public async Task ApproveCheckStock(CheckStockDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<CheckStock>(input, async (db, change) =>
            {
                await this.CheckStock(change, db);
            });
        }

        public async Task RevokeCheckStock(CheckStockDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<CheckStock>(input, async (db, change) =>
            {
                await this.CheckStock(change, db);
            });
        }

        async Task InoutWarehouse(InOutWarehousing changeEntity, DbContext db)
        {
            var warehouse = await db.Set<WarehouseGoods>()
                .OrderByDescending(x => x.addTime)
                .Where(x => x.goodsID == changeEntity.goodsID && x.warehouseID == changeEntity.warehouseID)
                .FirstOrDefaultAsync();

            var createNewWarehouseGoods = false;

            if (warehouse == null)
            {
                //如果当前门店没有关联商品，入库时则自动关联
                createNewWarehouseGoods = true;
                warehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = changeEntity.warehouseID
                };
                this.EntityHelper.InitEntity(warehouse);
            }

            if (changeEntity.isAuditing)
            {
                if (changeEntity.IsInWarehouse())
                {
                    warehouse.goodsNum += changeEntity.quantity;
                }
                else
                {
                    warehouse.goodsNum -= changeEntity.quantity;
                }
            }
            else
            {
                if (changeEntity.IsInWarehouse())
                {
                    warehouse.goodsNum -= changeEntity.quantity;
                }
                else
                {
                    warehouse.goodsNum += changeEntity.quantity;
                }
            }

            if (createNewWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(warehouse);
            }
        }

        async Task NotifyWarehouseGoodsCal(InOutWarehousing[] data)
        {
            var msgs = data.Select(x => new WarehouseGoodsCalMessage() { warehouseId = x.warehouseID, goodsId = x.goodsID }).ToArray();
            foreach (var m in msgs)
            {
                await this.newRetailEventBus.NotifyWarehouseGoodsCal(m);
            }
        }

        public async Task ApproveInoutWarehouse(InoutWarehouseDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty,
                isAutoPrint = dto.isAutoPrint
            };

            var res = await this.receiptsHelper.ApproveOrRevoke<InOutWarehousing>(input, async (db, change) =>
            {
                await this.InoutWarehouse(change, db);
            });

            await this.NotifyWarehouseGoodsCal(res);
        }

        public async Task<string> RevokeInoutWarehouse(InoutWarehouseDto dto)
        {
            #region 如果有销售了就不允许反审核 add by cl202302015
            string result = null;
            //如果有销售了就不允许反审核
            var db = await this.warehouseGoods.GetDbContextAsync();
            var inOutWarehouse = await db.Set<InOutWarehousing>()
                .Where(x => x.receiptNo == dto.receiptNo)
                .FirstOrDefaultAsync();
            if (inOutWarehouse != null)
            {
                //只有采购入库反审核才需要看是否有销售，退货不做限制
                if (inOutWarehouse.inOrOut == (int)InOrOutEnum.IN)
                {
                    var posSaleFlow = await db.Set<PosSalesFlow>()
                        .Where(x => x.goodsID == inOutWarehouse.goodsID && x.warehouseID == inOutWarehouse.warehouseID)
                        .FirstOrDefaultAsync();
                    if (posSaleFlow != null)
                    {
                        result = "存在销售不允许反审核！";
                        return result;
                    }
                }
            }
            #endregion

            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            var res = await this.receiptsHelper.ApproveOrRevoke<InOutWarehousing>(input, async (db, change) =>
            {
                await this.InoutWarehouse(change, db);
            });

            await this.NotifyWarehouseGoodsCal(res);

            return result;
        }

        async Task AdjustPrice(GoodsAdjustPrice changeEntity, DbContext db)
        {
            var warehouse = await db.Set<WarehouseGoods>()
                .OrderByDescending(x => x.addTime)
                .Where(x => x.goodsID == changeEntity.goodsID && x.warehouseID == changeEntity.warehouseID)
                .FirstOrDefaultAsync();

            var createWarehouseGoods = false;

            if (warehouse == null)
            {
                createWarehouseGoods = true;
                warehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = changeEntity.warehouseID
                };
                this.EntityHelper.InitEntity(warehouse);
            }

            if (changeEntity.isAuditing)
            {
                warehouse.price = changeEntity.newPrice;
                warehouse.updateTime = DateTime.Now;
            }
            else
            {
                warehouse.price = changeEntity.oldPrice;
            }

            if (createWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(warehouse);
            }
        }


        public async Task ApproveAdjustPrice(AdjustPriceDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<GoodsAdjustPrice>(input, async (db, change) =>
            {
                await this.AdjustPrice(change, db);
            });
        }

        public async Task RevokeAdjustPrice(AdjustPriceDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<GoodsAdjustPrice>(input, async (db, change) =>
            {
                await this.AdjustPrice(change, db);
            });
        }

        #region add by cl 其他单
        /// <summary>
        /// 其他单触发同步库存
        /// </summary>
        /// <param name="changeEntity"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        async Task OtherBill(OtherBill changeEntity, DbContext db)
        {
            var warehouse = await db.Set<WarehouseGoods>()
                .OrderByDescending(x => x.addTime)
                .Where(x => x.goodsID == changeEntity.goodsID && x.warehouseID == changeEntity.warehouseID)
                .FirstOrDefaultAsync();

            var createWarehouseGoods = false;

            if (warehouse == null)
            {
                createWarehouseGoods = true;
                warehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = changeEntity.warehouseID
                };
                this.EntityHelper.InitEntity(warehouse);
            }

            if (changeEntity.isAuditing)
            {
                //如果审核并且是出库则将库存相应减去数量
                if (changeEntity.inOrOut == (int)InOrOutEnum.OUT)
                    warehouse.goodsNum = warehouse.goodsNum - changeEntity.Num;
                else
                    warehouse.goodsNum = warehouse.goodsNum + changeEntity.Num;

                warehouse.costAmount = warehouse.costPrice * warehouse.goodsNum;
                warehouse.updateTime = DateTime.Now;
            }
            else
            {
                //如果反审则需要做反操作
                if (changeEntity.inOrOut == (int)InOrOutEnum.OUT)
                    warehouse.goodsNum = warehouse.goodsNum + changeEntity.Num;
                else
                    warehouse.goodsNum = warehouse.goodsNum - changeEntity.Num;

                warehouse.costAmount = warehouse.costPrice * warehouse.goodsNum;
                warehouse.updateTime = DateTime.Now;
            }

            if (createWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(warehouse);
            }
        }
        /// <summary>
        /// 其他单审核
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task ApproveOtherBill(OtherBillDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<OtherBill>(input, async (db, change) =>
            {
                await this.OtherBill(change, db);
            });
        }
        /// <summary>
        /// 其他单反审
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task RevokeOtherBill(OtherBillDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<OtherBill>(input, async (db, change) =>
            {
                await this.OtherBill(change, db);
            });
        }
        #endregion

        #region add by cl  次品单
        /// <summary> 
        /// 次品单触发同步库存
        /// </summary>
        /// <param name="changeEntity"></param>
        /// <param name="db"></param>
        /// <returns></returns>
        async Task DefectiveBill(DefectiveBill changeEntity, DbContext db)
        {
            var warehouse = await db.Set<WarehouseGoods>()
                .OrderByDescending(x => x.addTime)
                .Where(x => x.goodsID == changeEntity.goodsID && x.warehouseID == changeEntity.warehouseID)
                .FirstOrDefaultAsync();

            var createWarehouseGoods = false;

            if (warehouse == null)
            {
                createWarehouseGoods = true;
                warehouse = new WarehouseGoods()
                {
                    goodsID = changeEntity.goodsID,
                    warehouseID = changeEntity.warehouseID
                };
                this.EntityHelper.InitEntity(warehouse);
            }

            if (changeEntity.isAuditing)
            {
                //如果审核减去数量
                warehouse.goodsNum = warehouse.goodsNum - changeEntity.quantity;
                warehouse.costAmount = warehouse.costPrice * warehouse.goodsNum;
                warehouse.updateTime = DateTime.Now;
            }
            else
            {
                //如果反审则需要做反操作
                warehouse.goodsNum = warehouse.goodsNum + changeEntity.quantity;
                warehouse.costAmount = warehouse.costPrice * warehouse.goodsNum;
                warehouse.updateTime = DateTime.Now;
            }

            if (createWarehouseGoods)
            {
                db.Set<WarehouseGoods>().Add(warehouse);
            }
        }
        /// <summary>
        /// 次品单审核
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task ApproveDefectiveBill(DefectiveBillDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = true,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<DefectiveBill>(input, async (db, change) =>
            {
                await this.DefectiveBill(change, db);
            });
        }
        /// <summary>
        /// 次品单反审
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public async Task RevokeDefectiveBill(DefectiveBillDto dto)
        {
            var input = new ApproveOrRevoke()
            {
                receiptNo = dto.receiptNo,
                operatorId = dto.operatorId,
                isAuditing = false,
                remark = string.Empty
            };

            await this.receiptsHelper.ApproveOrRevoke<DefectiveBill>(input, async (db, change) =>
            {
                await this.DefectiveBill(change, db);
            });
        }
        #endregion

        #region add by cl 20220729

        #region 类别库存查询
        /// <summary>
        /// 类别库存查询
        /// </summary>
        /// <param name="inputDto"></param>
        /// <returns></returns>
        public async Task<IEnumerable<CategoryWarehouseGoodShowDto>> GetCategoryWarehouseGoodInfo(QueryCategoryWarehouseGoodDto inputDto)
        {
            //inputDto.Should().NotBeNullOrEmpty();

            var db = await this.warehouseGoods.GetDbContextAsync();
            var warehouseGoods = db.Set<WarehouseGoods>().AsNoTracking().Where(x => x.warehouseID == inputDto.warehouseID).ToList();
            var goodInfos = db.Set<GoodsInfo>().AsNoTracking().ToList();
            var goodCategorys = db.Set<GoodsCategory>().AsNoTracking().ToList();
            var warehouses = db.Set<Warehouse.Entity.Warehouse>().AsNoTracking().ToList();

            var result1 = warehouseGoods.GroupJoin(goodInfos, ct => ct.goodsID, cs => cs.Id, (x, y) => new { warehouseGoods = x, goodInfos = y })
                .SelectMany(xy => xy.goodInfos.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, goodInfos = y })
                .Select(s => new CategoryWarehouseGoodShowDto
                {
                    warehouseID = s.warehouseGoods?.warehouseID,
                    categoryID = s.goodInfos?.categoryID,
                    goodsNum = s.warehouseGoods.goodsNum,
                    costAmount = s.warehouseGoods.costAmount,
                    sellingPriceAmount = s.warehouseGoods.price * s.warehouseGoods.goodsNum,
                    profit = (s.warehouseGoods.price - s.warehouseGoods.costPrice) * s.warehouseGoods.goodsNum
                }).ToList();

            result1 = result1.Where(x => x.categoryID != null).ToList();
            //赋值顶级分类节点
            foreach (var item in result1)
            {
                var current = goodCategorys.Where(x => x.Id == item.categoryID).FirstOrDefault();
                //如果Pid有值表明有父节点
                if (current != null && current.pID != null)
                {
                    //获取到顶级节点
                    var topGoodCategory = GetParentGoodsCategorys(goodCategorys, current).Where(x => x.pID == null).FirstOrDefault();
                    item.categoryID = topGoodCategory.Id;
                }
            }


            var result = result1.GroupJoin(goodCategorys, ct => ct.categoryID, cs => cs.Id, (x, y) => new { warehouseGoods = x, goodCategorys = y })
               .SelectMany(xy => xy.goodCategorys.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, goodCategorys = y })
               .Select(s => new CategoryWarehouseGoodShowDto
               {
                   warehouseID = s.warehouseGoods.warehouseID,
                   categoryID = s.warehouseGoods.categoryID,
                   goodsNum = s.warehouseGoods.goodsNum,
                   costAmount = s.warehouseGoods.costAmount,
                   sellingPriceAmount = s.warehouseGoods.sellingPriceAmount,
                   profit = s.warehouseGoods.profit,
                   categoryNo = s.goodCategorys.No,
                   categoryName = s.goodCategorys.categoryName,
               }).ToList();
            var retObj = result.GroupBy(g => new { g.warehouseID, g.categoryID, g.categoryNo })
                .Select(x => new CategoryWarehouseGoodShowDto
                {
                    warehouseID = x.Key.warehouseID,
                    categoryID = x.Key.categoryID,
                    categoryNo = x.Key.categoryNo,
                    categoryName = x.Select(y => y.categoryName).FirstOrDefault(),
                    goodsNum = x.Sum(y => y.goodsNum),
                    costAmount = x.Sum(y => y.costAmount),
                    sellingPriceAmount = x.Sum(y => y.sellingPriceAmount),
                    profit = x.Sum(y => y.profit)
                })
                .OrderBy(x => x.warehouseID).ThenBy(x => x.categoryNo)
                .ToList();
            foreach (var item in retObj)
            {
                item.warehouseName = warehouses.FirstOrDefault(x => x.Id == item.warehouseID)?.warehouseName;
            }
            return retObj;
        }
        #endregion

        #region 获取current的所有父级
        /// <summary>
        /// 获取current的所有父级
        /// </summary>
        /// <param name="list"></param>
        /// <param name="current"></param>
        /// <returns></returns>
        private List<GoodsCategory> GetParentGoodsCategorys(List<GoodsCategory> list, GoodsCategory current)
        {
            var query = list.Where(x => x.Id == current.pID).ToList();
            return query.Concat(query.SelectMany(t => GetParentGoodsCategorys(list, t))).ToList();
        }
        #endregion

        #region 获取实时库存信息
        /// <summary>
        /// 获取实时库存信息
        /// </summary>
        /// <param name="inputDto"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<CategoryRealWarehouseGoodShowDto>> GetCategoryRealWarehouseGoodInfo(WarehouseGoodsPagingRequestDto inputDto)
        {
            var db = await this.warehouseGoods.GetDbContextAsync();
            var inOutWarehousings = db.Set<InOutWarehousing>().AsNoTracking().ToList();
            var goodInfos = db.Set<GoodsInfo>().AsNoTracking().Where(x => x.IsDeleted == false).ToList();
            var posSaleFlows = db.Set<PosSalesFlow>().AsNoTracking().ToList();
            var goodsCategorys = db.Set<GoodsCategory>().AsNoTracking().ToList();
            var warehouses = db.Set<NewRetail.Application.Components.Warehouse.Entity.Warehouse>().AsNoTracking().ToList();
            var suppliers = db.Set<Supplier>().AsNoTracking().ToList();
            //仓库条件过滤
            if (inputDto.HasWarehouseCondition())
            {
                if (!string.IsNullOrWhiteSpace(inputDto.warehouseName))
                {
                    warehouses = warehouses.Where(x => x.warehouseName.Contains(inputDto.warehouseName)).ToList();
                }
                if (!string.IsNullOrWhiteSpace(inputDto.warehouseNo))
                {
                    warehouses = warehouses.Where(x => x.No.Contains(inputDto.warehouseNo)).ToList();
                }

                var ids = warehouses.Select(x => x.Id);
                inOutWarehousings = inOutWarehousings.Where(x => ids.Contains(x.warehouseID)).ToList();
                posSaleFlows = posSaleFlows.Where(x => ids.Contains(x.warehouseID)).ToList();
            }
            //商品过滤条件
            if (inputDto.HasGoodsCondition())
            {
                if (!string.IsNullOrWhiteSpace(inputDto.goodsName))
                {
                    goodInfos = goodInfos.Where(x => x.goodsName.Contains(inputDto.goodsName)).ToList();
                }
                if (!string.IsNullOrWhiteSpace(inputDto.goodsNo))
                {
                    goodInfos = goodInfos.Where(x => x.No.Contains(inputDto.goodsNo)).ToList();
                }
                if (!string.IsNullOrWhiteSpace(inputDto.goodsBarCode))
                {
                    goodInfos = goodInfos.Where(x => x.barCode.Contains(inputDto.goodsBarCode)).ToList();
                }
                if (!string.IsNullOrWhiteSpace(inputDto.categoryNo))
                {
                    var category = goodsCategorys.FirstOrDefault(x => x.No == inputDto.categoryNo);
                    if (category != null)
                    {
                        goodInfos = goodInfos.Where(x => x.categoryID == category.Id).ToList();
                    }
                }
                var ids = goodInfos.Select(x => x.Id);
                inOutWarehousings = inOutWarehousings.Where(x => ids.Contains(x.goodsID)).ToList();
                posSaleFlows = posSaleFlows.Where(x => ids.Contains(x.goodsID)).ToList();
            }

            //获取对应供应商商品
            var supplierGoods = inOutWarehousings.GroupBy(g => new { g.warehouseID, g.goodsID, g.supplierID })
                .Select(x => new CategoryRealWarehouseGoodShowDto
                {
                    warehouseID = x.Key.warehouseID,
                    goodsID = x.Key.goodsID,
                    supplierID = x.Key.supplierID
                })
                .GroupJoin(suppliers, ct => ct.supplierID, cs => cs.Id, (x, y) => new { supplierGoods = x, supplierInfos = y })
               .SelectMany(xy => xy.supplierInfos.DefaultIfEmpty(), (x, y) => new CategoryRealWarehouseGoodShowDto
               {
                   warehouseID = x.supplierGoods.warehouseID,
                   goodsID = x.supplierGoods.goodsID,
                   supplierID = x.supplierGoods.supplierID,
                   Supplier = y
               }).ToList();
            //供应商过滤条件
            if (!string.IsNullOrWhiteSpace(inputDto.supplierId))
            {
                supplierGoods = supplierGoods.Where(x => x.supplierID.Contains(inputDto.supplierId)).ToList();
            }
            //分组获取入库/退货相关数量金额等
            var result0 = inOutWarehousings.GroupBy(g => new { g.warehouseID, g.goodsID })
                .Select(x => new CategoryRealWarehouseGoodShowDto
                {
                    warehouseID = x.Key.warehouseID,
                    goodsID = x.Key.goodsID,
                    quantity = x.Sum(s => s.inOrOut == (int)InOrOutEnum.IN ? s.quantity : -s.quantity),
                    grossProfit = x.Sum(s => s.inOrOut == (int)InOrOutEnum.IN ? s.grossProfit : -s.grossProfit),
                    costPrice = x.Average(s => s.price),
                    saleQuantity = 0,
                    saleMoney = 0,
                    discountMoney = 0
                }).ToList();
            //分组获取销售相关数量金额等
            var result1 = posSaleFlows.GroupBy(g => new { g.warehouseID, g.goodsID })
                .Select(x => new CategoryRealWarehouseGoodShowDto
                {
                    warehouseID = x.Key.warehouseID,
                    goodsID = x.Key.goodsID,
                    quantity = 0,
                    grossProfit = 0,
                    costPrice = 0,
                    saleQuantity = x.Sum(s => s.saleWay == SalesTypes.Refunds ? -s.saleQuantity : s.saleQuantity),
                    saleMoney = x.Sum(s => s.saleWay == SalesTypes.Refunds ? -s.saleMoney : s.saleMoney),
                    discountMoney = x.Sum(s => s.saleWay == SalesTypes.Refunds ? -s.discountMoney : s.discountMoney)
                }).ToList();
            //合并后分组计算入库销售等信息
            var result3 = result0.Union(result1).ToList().GroupBy(g => new { g.warehouseID, g.goodsID })
                .Select(x => new CategoryRealWarehouseGoodShowDto
                {
                    warehouseID = x.Key.warehouseID,
                    goodsID = x.Key.goodsID,
                    quantity = x.Sum(s => s.quantity),
                    grossProfit = x.Sum(s => s.grossProfit),
                    costPrice = x.Sum(s => s.costPrice),
                    saleQuantity = x.Sum(s => s.saleQuantity),
                    saleMoney = x.Sum(s => s.saleMoney),
                    discountMoney = x.Sum(s => s.discountMoney)
                }).ToList();
            //关联供应商
            var re0 = result3.GroupJoin(supplierGoods, ct => $"{ct.warehouseID}{ct.goodsID}", cs => $"{cs.warehouseID}{cs.goodsID}", (x, y) => new { warehouseGoods = x, supplierInfos = y })
               .SelectMany(xy => xy.supplierInfos.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = y?.Supplier });
            //关联商品
            var result4 = re0.GroupJoin(goodInfos, ct => ct.warehouseGoods.goodsID, cs => cs.Id, (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = y })
               .SelectMany(xy => xy.goodsInfos.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = y })
               .Where(e => e.goodsInfos != null);
            //关联分类
            var re5 = result4
               .GroupJoin(goodsCategorys, ct => ct.goodsInfos?.categoryID, cs => cs.Id, (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = x.goodsInfos, goodsCategorys = y })
               .SelectMany(xy => xy.goodsCategorys.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = x.goodsInfos, goodsCategorys = y });
            //关联仓库
            var re6 = re5
               .GroupJoin(warehouses, ct => ct.warehouseGoods?.warehouseID, cs => cs.Id, (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = x.goodsInfos, goodsCategorys = x.goodsCategorys, warehouses = y })
               .SelectMany(xy => xy.warehouses.DefaultIfEmpty(), (x, y) => new { warehouseGoods = x.warehouseGoods, supplierInfos = x.supplierInfos, goodsInfos = x.goodsInfos, goodsCategorys = x.goodsCategorys, warehouses = y });
            var result = re6
               .GroupBy(g => new
               {
                   g.warehouseGoods.warehouseID,
                   g.warehouseGoods.goodsID,
                   g.supplierInfos?.Id,
                   g.goodsInfos?.categoryID
               })
                .Select(s => new CategoryRealWarehouseGoodShowDto
                {
                    warehouseID = s.Key.warehouseID,
                    goodsID = s.Key.goodsID,
                    categoryID = s.Key.categoryID,
                    supplierID = s.Key.Id,
                    categoryName = s.Max(y => y.goodsCategorys?.categoryName),
                    categoryNo = s.Max(y => y.goodsCategorys?.No),
                    price = s.Sum(y => y.goodsInfos == null ? 0 : y.goodsInfos.price),
                    costPrice = s.Sum(y => y.warehouseGoods.costPrice),
                    costAmount = s.Sum(y => (y.warehouseGoods.costPrice) * (y.warehouseGoods.quantity - y.warehouseGoods.saleQuantity)),
                    goodsNum = s.Sum(y => y.warehouseGoods.quantity - y.warehouseGoods.saleQuantity),
                    quantity = s.Sum(y => y.warehouseGoods.quantity),
                    grossProfit = s.Sum(y => y.warehouseGoods.grossProfit),
                    saleQuantity = s.Sum(y => y.warehouseGoods.saleQuantity),
                    saleMoney = s.Sum(y => y.warehouseGoods.saleMoney),
                    discountMoney = s.Sum(y => y.warehouseGoods.discountMoney),
                    GoodsInfo = s.FirstOrDefault().goodsInfos,
                    GoodsCategory = s.FirstOrDefault().goodsCategorys,
                    Warehouse = s.FirstOrDefault().warehouses,
                    Supplier = s.FirstOrDefault().supplierInfos
                }).ToList();

            var response = new PagedResultDto<CategoryRealWarehouseGoodShowDto>();
            response.TotalCount = result.Count();
            int pageSize = inputDto.PageSize == 0 ? 100 : inputDto.PageSize, pageIndex = inputDto.Page == 0 ? 1 : inputDto.Page;
            //总页数
            var pageCount = (result.Count() + pageSize - 1) / pageSize;
            result = result.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList();

            response.Items = result;

            return response;

        }
        #endregion

        #region 附加供应商和分类
        //public async Task<WarehouseGoods[]> AttachSupplierCategorys(WarehouseGoods[] data)
        //{
        //    var db = await this.warehouseGoods.GetDbContextAsync();
        //    var inOutWarehousings = db.Set<InOutWarehousing>().AsNoTracking();
        //    var goodInfos = db.Set<GoodsInfo>().AsNoTracking();
        //    var ret0 = inOutWarehousings.GroupBy(g => new { g.warehouseID, g.goodsID, g.supplierID })
        //        .Select(x => new
        //        {
        //            warehouseID = x.Key.warehouseID,
        //            goodsID = x.Key.goodsID,
        //            supplierID = x.Key.supplierID,
        //        }).GroupJoin(goodInfos, ct => ct.goodsID, cs => cs.Id, (x, y) => new { inOutWarehouseGood = x, goodInfos = y })
        //        .SelectMany(xy => xy.goodInfos.DefaultIfEmpty(), (x, y) => new { inOutWarehouseGood = x.inOutWarehouseGood, goodInfos = y })
        //        .Select(s => new
        //        {
        //            warehouseID = s.inOutWarehouseGood.warehouseID,
        //            goodsID = s.inOutWarehouseGood.goodsID,
        //            supplierID = s.inOutWarehouseGood.supplierID,
        //            categoryID = s.goodInfos.categoryID
        //        });
        //    data = data.GroupJoin(ret0, ct => new { ct.warehouseID, ct.goodsID }, cs => new { cs.warehouseID, cs.goodsID },
        //        (x, y) => new { warehouseGood = x, warehouseGoodsSuppliers = y })
        //       .SelectMany(xy => xy.warehouseGoodsSuppliers.DefaultIfEmpty(), (x, y) => new { warehouseGood = x.warehouseGood, warehouseGoodsSuppliers = y })
        //       .Select(s => new WarehouseGoods
        //       {
        //           warehouseID = s.warehouseGood.warehouseID,
        //           goodsID = s.warehouseGood.goodsID,
        //           goodsNum = s.warehouseGood.goodsNum,

        //           supplierID = s.warehouseGoodsSuppliers.supplierID
        //       });
        //}
        #endregion

        #endregion
    }
}
